﻿package com.sff.manager {
	
	import com.sff.utils.ArrayCollection;
	import com.sff.core.ILoadable;
	import com.sff.core.IProgressable;
	import com.sff.events.LoaderErrorEvent;
	import com.sff.events.LoaderEvent;
	import com.sff.events.LoaderProgressEvent;
	import com.sff.events.LoaderStatusEvent;
	
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.ProgressEvent;
	import flash.events.StatusEvent;
	
	public class LoaderManager extends EventDispatcher implements IProgressable, ILoadable {
		
		protected var _item 			: ILoadable;
		protected var _statusMessage 	: String;		
		protected var _collection 		: ArrayCollection;
		protected var _isRunning 		: Boolean = false;
		protected var _run 				: Boolean = false;
		protected var _startLen 		: Number = 0;
		
		public function LoaderManager(statusMessage : String = null ) {
			_collection = new ArrayCollection(null);
			_statusMessage = statusMessage;
		}
		
		//public function set type( val : Number) : void { 
		//	_collection.type = val;
		//}
		//public function get type() : Number { 
		//	return _collection.type ; 
		//}
		
		public function getCurrentItem() : ILoadable {
			return _item;
		}
		
		public function addItem( item : ILoadable ) : void {
			if( _collection.addItem( item ) > -1 ) {
				itemAddingIn( item );
			}
			next();
		}
		
		public function addItemAt( item : ILoadable, index : uint ) : void {
			_collection.addItemAt( item, index );
			
			if( _collection.indexOf(item) == -1) {
				itemAddingIn( item );
			}
			
			next( ); 
		}
		
		public function removeItem ( item : ILoadable ) : int {
			var index : int = _collection.removeItem( item );
			if( index > -1 ) itemRemoving( item );
			return index;
		}
		
		public function removeItemAt ( index : uint ) : ILoadable {
			var item : ILoadable = _collection.removeItemAt(index) as ILoadable;
			if( item ) itemRemoving( item );
			return item;
		}
		
		public function setItemIndex ( item : ILoadable , index : uint ) : void {
			_collection.setItemIndex(item, index);
		}
		
		public function swapItems ( item1 : ILoadable, item2 : ILoadable ) : void {
			_collection.swapItems(item1, item2);
		}
		
		public function swapItemsAt ( index1 : uint, index2 : uint ) : void {
			_collection.swapItemsAt(index1, index2);
		}
		
		public function indexOf( item : ILoadable, fromIndex : int = 0 ) : int {
			return _collection.indexOf( item, fromIndex );
		}
		
		public function getItemAt ( index : uint ) : ILoadable {
			return _collection.getItemAt(index) as ILoadable;
		}
		
		public function replaceItem( oldItem : ILoadable, newItem : ILoadable ) : void {
			if( newItem == null ) removeItem( oldItem );
			
			else if( _collection.replaceItem( oldItem, newItem ) ) {
				itemAddingIn( newItem );
				itemRemoving( oldItem );
			}
		}

		public function start() : void {
			_startLen = getLength( );
			if( _run ) return;
			sendOpen( new Event( Event.OPEN ) );
			_run = true;
			next( );
		}
		
		public function stop() : void {
			_run = false;
		}
		
		public function clear() : void {
			while ( _collection.hasNext( ) ) _collection.next( );
			
			if( _item != null ) {
				unregisterItem( _item );
				_item.dispose( );
			}
			
			dispatchEvent( new LoaderEvent( LoaderEvent.DISPOSED, this ) );
		}
		
		public function getLength() : uint {
			var len : uint = 0;
			
			for ( var i : int = 0; i < _collection.length; i++ ) {
				len += _collection.getItemAt(i).getLength();
			}
			
			if( _item ) {
				len += _item.getLength( );
			}
			
			return len;
		}
		
		private function next() : void {
			if( _isRunning || ! _run ) return;
			if( _isRunning = _collection.hasNext() ) {
				_item = ILoadable( _collection.next( ) );
				
				if( !_item ) next();
				registerItem( _item );
				dispatchEvent( new LoaderEvent( LoaderEvent.ITEM_START, _item ) );
				_item.execute( );
			}
			else {
				_item = null;
				sendComplete( new Event( Event.COMPLETE ) );	
			}
		}
		
		private function registerItem( item : ILoadable ) : void {
			item.addEventListener( Event.COMPLETE					, onItemComplete );
			item.addEventListener( LoaderEvent.ITEM_COMPLETE		, onSubComplete );
			item.addEventListener( LoaderProgressEvent.ITEM_PROGRESS, onItemProgress );
			item.addEventListener( LoaderEvent.ITEM_START			, onItemStart );
			item.addEventListener( ErrorEvent.ERROR					, onItemError );
			item.addEventListener( StatusEvent.STATUS				, onItemStatus );
			item.addEventListener( LoaderEvent.DISPOSED				, onCurrentItemDisposed );
		}
		
		private function unregisterItem( item : ILoadable ) : void {
			item.removeEventListener( Event.COMPLETE					, onItemComplete );
			item.removeEventListener( LoaderEvent.ITEM_COMPLETE			, onSubComplete );
			item.removeEventListener( LoaderProgressEvent.ITEM_PROGRESS	, onItemProgress );
			item.removeEventListener( LoaderEvent.ITEM_START			, onItemStart );
			item.removeEventListener( ErrorEvent.ERROR					, onItemError );
			item.removeEventListener( StatusEvent.STATUS				, onItemStatus );
			item.removeEventListener( LoaderEvent.DISPOSED				, onCurrentItemDisposed );
		}
		
		private function itemAddingIn( item : ILoadable ) : void {
			item.dispatchEvent( new LoaderEvent( LoaderEvent.ADDED, item ) );
			
			item.addEventListener( LoaderEvent.ADDED, itemAddingOut );
			item.addEventListener( LoaderEvent.DISPOSED, onItemDisposed );
		}
		
		private function itemAddingOut (event : LoaderEvent) : void {
			removeItem( event.target as ILoadable );
		}		

		private function itemRemoving( item : ILoadable ) : void {
			item.dispatchEvent( new LoaderEvent( LoaderEvent.REMOVED, item ) );
			item.removeEventListener( LoaderEvent.ADDED, itemAddingOut );
			item.removeEventListener( LoaderEvent.DISPOSED, onItemDisposed );
		}
		
		protected function onItemComplete( e : Event ) : void {
			
			dispatchEvent( new LoaderEvent( LoaderEvent.ITEM_COMPLETE, _item ) );
			
			if( _item ) {
				unregisterItem( _item );
				_item.dispatchEvent( new LoaderEvent ( LoaderEvent.REMOVED, this ) );
			}
			
			_isRunning = false;
			next( );
		}

		protected function onItemDisposed( e : LoaderEvent ) : void {
			var item : ILoadable = e.currentTarget as ILoadable;
			removeItem( item );
		}

		protected function onCurrentItemDisposed( e : LoaderEvent ) : void {
			var item : ILoadable = e.currentTarget as ILoadable;
			
			unregisterItem( item );
			_isRunning = false;
			if( _collection.hasNext() ) {
				next( );
			}
		}

		protected function onSubComplete( e : LoaderEvent ) : void {
			dispatchEvent( new LoaderEvent( LoaderEvent.ITEM_COMPLETE, e.item ) );
		}
		
		protected function onItemStart( e : LoaderEvent ) : void {
			dispatchEvent( new LoaderEvent( LoaderEvent.ITEM_START, e.item ) );
		}
		
		protected function onItemProgress( e : LoaderProgressEvent ) : void {
			var ppart : Number = e.bytesLoaded / e.bytesTotal * e.pond;
			var  l : uint = getLength();
			if( _startLen < l ) _startLen = l;
			sendProgress( new ProgressEvent( ProgressEvent.PROGRESS , false, false, ( 1 - ( l - ppart ) / _startLen ) * 1000, 1000 ) );
			dispatchEvent( e );
		}
		
		protected function onItemError ( e : LoaderErrorEvent ) : void {
			sendError( new LoaderErrorEvent( ErrorEvent.ERROR , e.text, e.item) );
			
			if( ! ( _item is LoaderManager ) ) onItemComplete( null );
		}

		protected function onItemStatus ( e : LoaderStatusEvent ) : void {
			sendStatus(  new LoaderStatusEvent ( StatusEvent.STATUS, _statusMessage || "batch run" , e.messages));
		}
				
		public function sendStatus	( e : StatusEvent ) 	: void { dispatchEvent( e );	}		
		
		public function sendProgress( e : ProgressEvent	) 	: void { dispatchEvent( e ); }		
		
		public function sendError	( e : ErrorEvent ) 		: void { dispatchEvent( e ); }		
		
		public function sendOpen 	( e : Event ) 			: void { dispatchEvent( e );}
		
		public function sendComplete ( e : Event )			: void { dispatchEvent( e );}
		
		public function execute() : void {	start( ); }
		
		public function dispose() : void {	clear( ); }
	}
}
